//--------------------------------------------------------------------------------
// This file is part of the Windows Workflow Foundation Sample Code
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is intended only as a supplement to Microsoft
// Development Tools and/or on-line documentation.  See these other
// materials for detailed information regarding Microsoft code samples.
// 
// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//--------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Collections.ObjectModel;

namespace Microsoft.Samples.Workflow.UI
{
    public class SynchronousSchedulerService : WorkflowSchedulerService
    {
        Dictionary<Guid, TimerInfo> timerInfos = new Dictionary<Guid, TimerInfo>();

        public SynchronousSchedulerService()
        {
        }

        internal WorkflowRuntime WorkflowRuntime 
        { 
            get 
            { 
                return this.Runtime; 
            } 
        }

        protected override void Schedule(WaitCallback callback, Guid workflowInstanceId)
        {
            if (SynchronizationContext.Current != null)
            {
                SynchronizationContext.Current.Send(delegate(object o2) { callback(workflowInstanceId); }, null);
            }
            else
            {
                callback(workflowInstanceId);
            }
        }

        protected override void Schedule(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId)
        {
            DateTime now = DateTime.UtcNow;
            TimeSpan span = (whenUtc > now) ? whenUtc - now : TimeSpan.Zero;
            TimerInfo ti = new TimerInfo(this, timerId, workflowInstanceId, callback, span);
            this.timerInfos.Add(timerId, ti);
        }

        protected override void Cancel(Guid timerGuid)
        {
            ((IDisposable)this.timerInfos[timerGuid]).Dispose();
            this.timerInfos.Remove(timerGuid);
        }

        protected override void OnStopped()
        {
            foreach (TimerInfo ti in this.timerInfos.Values)
            {
                ((IDisposable)ti).Dispose();
            }

            this.timerInfos.Clear();
            base.OnStopped();
        }

        //each timer created by an activity has an associated TimerInfo
        class TimerInfo : IDisposable
        {

            private SynchronousSchedulerService threadingService = null;
            private TimerCallback timerCallback = null;
            private Guid instanceid;
            private WaitCallback callback;
            private Timer timer = null;

            private TimerInfo() { }

            internal TimerInfo(SynchronousSchedulerService threadingService, Guid timerid, Guid instanceid, WaitCallback callback, TimeSpan span)
            {
                this.instanceid = instanceid;
                this.callback = callback;
                this.timerCallback = new TimerCallback(OnTimerCallback);
                this.timer = new Timer(this.timerCallback, timerid, span, new TimeSpan(Timeout.Infinite));
                this.threadingService = threadingService;
            }

            void OnTimerCallback(object timerid)
            {
                WorkflowInstance inst = this.threadingService.WorkflowRuntime.GetWorkflow(this.instanceid);
                threadingService.Schedule(this.callback, inst.InstanceId);
            }

            void IDisposable.Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool disposing)
            {
                if (disposing)
                {
                    if (this.timer != null)
                    {
                        this.timer.Dispose();
                        this.timer = null;
                    }
                }
            }
        }
    }
}